#!/usr/bin/env python
import os
import os.path
import sys
import copy

from Ganga.Utility.logging import getLogger

from AutoD3PDMakerTask import AutoD3PDMakerTask
from AutoD3PDMakerUtility import table_output

from AutoD3PDMakerTaskManager import getNewTasksFromFile, shapeNewTasks

class AutoD3PDMakerUserInterface:

    '''
    User interface class providing methods for searching, listing, adding and deleting tasks through the task manager.
    '''

    def __init__(self, taskManager):
        self.logger = getLogger('AutoD3PDMakerUserInterface')
        self.taskManager = taskManager

    def __printTaskList__(self, attrList, taskList, fobj=None, fmt='table'):
        """
        common method for making pretty print on the given task list. The attributes
        to be printed is given by attrList in terms of attribute names.
        """

        title = 'no. tasks: %d' % len(taskList)

        dataList = []

        for t in taskList:
            data = {}

            for attr in attrList:
                val = t
                try:
                    for key in attr.split('.'):
                        val = val[key]
                except KeyError:
                    val = 'N/A'

                ## if val is still equal to t, it means even the first level attr is unavailable
                if val != t:
                    data[attr] = val

            dataList.append(data)

        if not fobj:
            fobj = sys.stdout

        if fmt in ['table']:
            table_output(title, attrList, dataList, fobj)

        return

    def listTasks(self, clauses={}, attrs=[], fmt='table'):
        """
        selects and lists the tasks in a summary table.

        e.g. listTasks(clauses = {'status':'finished'},
                       attrs   = ['gtId','status','metadata.version'])
        """

        if not attrs:
            attrs = ['gtId', 'status', 'input', 'output.ds']

        taskList = self.taskManager.selectTasks(clauses)

        self.__printTaskList__(attrs, taskList, sys.stdout, fmt)

    def copyTasks(self, clauses={}):
        """
        makes new tasks by deep-copying the existing tasks matching the searching clauses.

        e.g. selectTasks(clauses = {'status':'running'})

        """

        taskList = map( lambda x:repr(x), self.taskManager.selectTasks(clauses) )

        return shapeNewTasks( taskList )


    def rerunTasks(self, clauses):
        """
        re-runs the tasks matching the given searching clauses.

        This will remove the GangaTasks/jobs currently associated with the tasks and reset task to status 'new'
        """
        return self.taskManager.resetTasks(clauses, status=AutoD3PDMakerTask.status_map['new'])

    def reppTasks(self, clauses, force=False):
        """
        re-runs the post-processing on the tasks matching the given searching clauses.

        This only applies on the tasks on 'unfinished' status.
        """

        taskList = self.taskManager.selectTasks(clauses)

        trepped = []

        for t in taskList:
            if ( t.status in AutoD3PDMakerTask.status_map['unfinished'] ) or force:
                self.taskManager.resetTasks(clauses, status=AutoD3PDMakerTask.status_map['completed'])
                trepped.append(t)

        return trepped

    def loadTasks(self, taskListFile):
        """
        loads new tasks given in a task list file.
        """

        if not os.path.exists(taskListFile):
            self.logger.error('file not found: %s' % taskListFile)
            return False

        taskList = getNewTasksFromFile(taskListFile)

        return self.addTasks(taskList)

    def addTasks(self, taskList):
        """
        adds new tasks into the system.
        """

        goodTasks = []

        badTasks  = []

        (goodTasks,badTasks) = self.taskManager.verifyTasks(taskList)

        for t in goodTasks:
            self.taskManager.addNewTask(t, persistency=True)

        if len(badTasks) > 0:
            self.logger.warning( '%d tasks defined with bad descriptions' % len(badTasks) )

        self.logger.info('%d tasks added' % len(goodTasks))

        return True

    def resumeTasks(self, clauses={}):
        """
        resumes tasks matching the given search clauses.

         - the pause tasks go to 'running' status
         - the unfinished tasks go to 'finishing' status
        """

        tlist   = self.taskManager.selectTasks(clauses)

        tresumed = []

        for t in tlist:
            if t.status == AutoD3PDMakerTask.status_map['pause']:
                self.taskManager.resetTasks({'id':t.id}, status=AutoD3PDMakerTask.status_map['running'])
                tresumed.append(t)

            elif t.status == AutoD3PDMakerTask.status_map['unfinished']:
                self.taskManager.resetTasks({'id':t.id}, status=AutoD3PDMakerTask.status_map['finishing'])
                tresumed.append(t)

            else:
                self.logger.error('task %s not in "%s" or "%s" status' % (t.id,
                                                                          AutoD3PDMakerTask.status_map['pause'],
                                                                          AutoD3PDMakerTask.status_map['unfinished']))

        return tresumed

    def pauseTasks(self, clauses={}):
        """
        pauses tasks matching the given searching clauses.

         - the running tasks go to 'pause' status
         - the finishing tasks go to 'unfinished' status
        """

        tlist   = self.taskManager.selectTasks(clauses)

        tpaused = []

        for t in tlist:
            if t.status == AutoD3PDMakerTask.status_map['running']:
                self.taskManager.resetTasks({'id':t.id}, status=AutoD3PDMakerTask.status_map['pause'])
                tpaused.append(t)

            elif t.status == AutoD3PDMakerTask.status_map['finishing']:
                self.taskManager.resetTasks({'id':t.id}, status=AutoD3PDMakerTask.status_map['unfinished'])
                tpaused.append(t)

            else:
                self.logger.error('Task %s not in "%s" or "%s" status' % (t.id,
                                                                          AutoD3PDMakerTask.status_map['running'],
                                                                          AutoD3PDMakerTask.status_map['finishing']))

        return tpaused

    def removeTasks(self, clauses={}, deep=True):
        """
        removes tasks matching the given searching clauses.

        The default 'deep' mode deletion removes also the produced ntuples from local disk as
        well as the associated GangaTasks and Ganga jobs.
        """

        stlist,ftlist = self.taskManager.removeTasks(clauses, deep)

        self.logger.info('%d tasks removed' % len(stlist))

        if len( ftlist ) > 0:
            self.logger.error('failed to remove tasks: %s' % ','.join( map(lambda t:t.id, ftlist) ) )

        return
